package memory;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

import kernel.CPU;

import adt.CPUprocess;
import adt.ConsumerProcess;

public class Semaphore extends Thread
{
	/**
	 * Semaphore blocked queue, processes that need to read/write to the shard memory. 
	 * But does not have access to it at that time.
	 */
	private BlockingQueue<CPUprocess> my_semblocked_queue = new LinkedBlockingQueue<CPUprocess>();
	
	/**
	 * The semaphore owner. 
	 * The process that has right to read/write to the semaphore.
	 */
	private CPUprocess my_sem_owner;
	
	/**
	 * associated CPU.
	 */
	private CPU my_cpu;
	
	public Semaphore(CPU the_cpu)
	{
		my_cpu = the_cpu;
	}
	
	/**
	 * read the shared memory. 
	 * @param the_process the process that generates the sys_rd_request.
	 * @return true if the process can have access to the shared memory.
	 */
	public boolean sysread(CPUprocess the_process) 
	{
		boolean result = ((my_sem_owner == null) || my_sem_owner.equals(the_process));
		
		if(result)
		{
			my_sem_owner = the_process;
		}
		else
		{
			my_semblocked_queue.add(the_process);
		}
		
		return result;
	}
	
	/**
	 * write the shared memory. 
	 * @param the_process the process that generates the sys_write_request.
	 * @return true if the process can have access to the shared memory.
	 */
	public boolean syswrite(CPUprocess the_process) 
	{
		boolean result = ((my_sem_owner == null) || my_sem_owner.equals(the_process));
		if (result)
		{
			my_sem_owner = the_process;
		}
		else
		{
			my_semblocked_queue.add(the_process);
		}
		
		return result;
	}
	
	/**
	 * output helper class determine what type of action the semaphore owner might perform.
	 * @return "reading" / "writing" depends on the type of process.
	 */
	public String getAction()
	{
		return my_sem_owner.getClass().equals(new ConsumerProcess((int)Math.random()).getClass()) ? "reading" : "writing";
	}
	
	/**
	 * free up the semaphore ownership.
	 * if the blocked queue contains processes. the head of the queue get to own the semaphore.
	 */
	public void semUp()
	{
		if (my_semblocked_queue.size()!= 0)
		{
			CPUprocess temp = my_semblocked_queue.poll();
			System.out.println("Semaphore Owner is switching from " + my_sem_owner.toString() + " to "+
					temp.toString());
			my_sem_owner = temp;
			my_cpu.process_ready(temp);			
			//interrupt();
		}
		else 
		{
			if(my_sem_owner !=  null)
				System.out.println(my_sem_owner.toString() + " is giving up Semaphore.");
			
			my_sem_owner = null;
			//interrupt();
			
		}
	}
	
	
	/**
	 * 
	 * @return the current owner of the semaphore.
	 */
	public CPUprocess getOwner() {
		return my_sem_owner;
	}
	
	
	/**
	 * constantly checking if the semaphore owner need to perform a read/write request. 
	 * if it does this thread will pause for 8 sec. (the time the memory access takes.)
	 */
	public void run()
	{

		while(true)
		{
			if (getOwner()!=null && getOwner().equals(my_cpu.getCurrentProcess()))
			{
				System.out.println(getOwner().toString() + " is " + getAction() + " the memory.");
				try {
					Thread.sleep(8000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				semUp();
			}
		}
	}
	
	/**
	 * display the owner of the semaphore and the sempahore blocked queues' state.
	 */
	public void display() {
		if(my_sem_owner != null)
		{
			System.out.println("\nMy Semaphore Owner: " + my_sem_owner.toString());
		}
		System.out.println("\nSemaphore Blocked Queue:");
		for(CPUprocess p : my_semblocked_queue)
		{
			System.out.print(p.getPid() + ", ");
		}
		
	}
}
